package driver.actions;

import driver.ActionDriver;
import network.managers.ObserverManager;
import network.udp.UDPMessageManager;
import messages.BaseMessage;
import messages.CString;
import messages.GeneralAck;
import state.group.events.BaseEvent;
import state.person.Person;
import state.person.PersonState;
import state.person.events.DestinationEvent;
import state.person.events.GoingInEvent;
import state.person.events.GoingOutEvent;
import state.person.events.InsideEvent;
import state.person.events.WaitingEvent;
import system.GlobalData;

public class StandardPersonAction extends BaseAction {

    private byte elevatorId;
    private Person p;
    private PersonState successState;
    private BaseMessage message;
    private StandardPersonAction nextAction;
    private long nextActionDelay;
    
    public StandardPersonAction(byte elevatorId, Person p, PersonState successState, BaseMessage message)
    {
        this.elevatorId = elevatorId;
        this.p = p;
        this.successState = successState;
        this.message = message;
        this.nextAction = null;
        this.nextActionDelay = 0;
    }
    
    public StandardPersonAction(byte elevatorId, Person p, PersonState successState, BaseMessage message, StandardPersonAction nextAction, long nextActionDelay)
    {
        this.elevatorId = elevatorId;
        this.p = p;
        this.successState = successState;
        this.message = message;
        this.nextAction = nextAction;
        this.nextActionDelay = nextActionDelay;
    }
    
    @Override
    public void performAction()
    {        
        
        GeneralAck ack = UDPMessageManager.sendMessage(elevatorId, message);
        
        synchronized (p)
        {
            if (ack != null && ack.getAckMessageType() == message.getMessageType() && ack.getSuccess())
            {
                if (p.getState() != successState)
                {
                    p.setState(successState);
                    
                    BaseEvent event = null;
                    
                    switch (successState)
                    {
                        case waiting:
                            byte floor;
                            synchronized (GlobalData.partialState)
                            {
                                floor = GlobalData.partialState.get(elevatorId).getLastKnownFloor();
                            }
                            event = new WaitingEvent(new CString(p.getName()));
                            break;
                        case goingIn:
                            event = new GoingInEvent(new CString(p.getName()), elevatorId);
                            p.setCurrentElevatorId(elevatorId);
                            break;
                        case inside:
                            event = new InsideEvent(new CString(p.getName()));
                            break;
                        case goingOut:
                            synchronized (GlobalData.partialState)
                            {
                                floor = GlobalData.partialState.get(elevatorId).getLastKnownFloor();
                            }
                            event = new GoingOutEvent(new CString(p.getName()), floor);
                            p.setCurrentElevatorId((byte) 0);
                            break;
                        case atDestination:
                            event = new DestinationEvent(new CString(p.getName()));
                            break;
                    }
                    
                    ObserverManager.getInstance().postEvent(event);
                }
                
                if (nextAction != null)
                {
                    ActionDriver.getInstance().executeAction(nextActionDelay, nextAction);
                }
                else
                {
                    p.setFree();
                }
            }
            else
            {
                p.setFree();
            }
        }
        
        boolean shouldTransfer;
        
        synchronized (p)
        {
            shouldTransfer = p.getState() == PersonState.atDestination;
        }
        
        if (shouldTransfer)
        {
            synchronized (GlobalData.state.activePeople)
            {
                synchronized (GlobalData.state.deactivatedPeople)
                {
                    GlobalData.state.activePeople.remove(p);
                    GlobalData.state.deactivatedPeople.add(p);
                }
            }
        }
    }
    
    @Override
    public String toString()
    {
        return "<StandardPersonAction p=" + p.getName() + "/>";
    }
}
